package tribe

import (
	"crypto/ecdsa"
	"errors"
	"fmt"
	"math/big"
	"sync"
	"time"

	"github.com/yunhailanuxgk/go-jinbao/accounts"
	"github.com/yunhailanuxgk/go-jinbao/common"
	"github.com/yunhailanuxgk/go-jinbao/common/hexutil"
	"github.com/yunhailanuxgk/go-jinbao/consensus"
	"github.com/yunhailanuxgk/go-jinbao/core/types"
	"github.com/yunhailanuxgk/go-jinbao/params"
	lru "github.com/hashicorp/golang-lru"
)

const (
	// None -> Volunteer -> Signer -> Sinner
	LevelNone      = "None"
	LevelVolunteer = "Volunteer"
	LevelSigner    = "Signer"
	LevelSinner    = "Sinner" //黑名单

	historyLimit = 2048
	wiggleTime   = 500 * time.Millisecond // Random delay (per signer) to allow concurrent signers
	CHIEF_NUMBER = int64(30)              //最早版本chief合约开始生效的块数
)

var (
	blockPeriod  = uint64(15)                               // Default minimum difference between two consecutive block's timestamps
	_extraVanity = 32                                       // Fixed number of extra-data prefix bytes reserved for signer vanity
	_extraVrf    = 161                                      // before SIP100 extra format is bytes[extraVanity+extraSeal], after is bytes[extraVrf+extraSeal]
	extraSeal    = 65                                       // Fixed number of extra-data suffix bytes reserved for signer seal
	nonceSync    = hexutil.MustDecode("0xffffffffffffffff") // TODO Reserved to control behavior
	nonceAsync   = hexutil.MustDecode("0x0000000000000000") // TODO Reserved to control behavior
	uncleHash    = types.CalcUncleHash(nil)                 // Always Keccak256(RLP([])) as uncles are meaningless outside of PoW.
	// less than SIP100 >>>>>>>>>>>>>
	// new role is in turn 6 ~ 1
	diffInTurnMain = big.NewInt(3) // Block difficulty for in-turn Main
	diffInTurn     = big.NewInt(2) // Block difficulty for in-turn Sub
	diffNoTurn     = big.NewInt(1) // Block difficulty for out-of-turn Other
	// less than SIP100 <<<<<<<<<<<<<
	diff = int64(6) // SIP100 max diff is 6

	extraVanityFn = func(num *big.Int) int {
		if params.IsSIP100Block(num) {
			return _extraVrf
		}
		return _extraVanity
	}
)

// Various error messages to mark blocks invalid. These should be private to
// prevent engine specific errors from being referenced in the remainder of the
// codebase, inherently breaking if the engine is swapped out. Please put common
// error types into the consensus package.
var (
	// errUnknownBlock is returned when the list of signers is requested for a block
	// that is not part of the local blockchain.
	errUnknownBlock = errors.New("unknown block")

	// errInvalidCheckpointBeneficiary is returned if a checkpoint/epoch transition
	// block has a beneficiary set to non-zeroes.
	errInvalidCheckpointBeneficiary = errors.New("beneficiary in checkpoint block non-zero")

	// only accept sync or async flag
	// allowed constants of 0x00..0 or 0xff..f.
	errInvalidNonce = errors.New("nonce not 0x00..0 or 0xff..f")

	// errMissingVanity is returned if a block's extra-data section is shorter than
	// 32 bytes, which is required to store the signer vanity.
	errMissingVanity = errors.New("extra-data 32 byte vanity prefix missing")

	// errMissingSignature is returned if a block's extra-data section doesn't seem
	// to contain a 65 byte secp256k1 signature.
	errMissingSignature = errors.New("extra-data 65 byte suffix signature missing")

	// errExtraSigners is returned if non-checkpoint block contain signer data in
	// their extra-data fields.
	errExtraSigners = errors.New("non-checkpoint block contains extra signer list")

	// errInvalidMixDigest is returned if a block's mix digest is non-zero.
	errInvalidMixDigest = errors.New("non-zero mix digest")

	// errInvalidUncleHash is returned if a block contains an non-empty uncle list.
	errInvalidUncleHash = errors.New("non empty uncle hash")

	// errInvalidDifficulty is returned if the difficulty of a block is not either
	// of 1 - 3 , or if the value does not match the turn of the signer.
	errInvalidDifficulty = errors.New("invalid__difficulty")

	// ErrInvalidTimestamp is returned if the timestamp of a block is lower than
	// the previous block's timestamp + the minimum block period.
	ErrInvalidTimestamp       = errors.New("invalid timestamp")
	ErrInvalidTimestampSIP002 = errors.New("invalid timestamp (SIP002)")

	// errUnauthorized is returned if a header is signed by a non-authorized entity.
	errUnauthorized = errors.New("unauthorized")

	// errWaitTransactions is returned if an empty block is attempted to be sealed
	// on an instant chain (0 second period). It's important to refuse these as the
	// block reward is zero, so an empty block just bloats the chain... fast.
	errWaitTransactions = errors.New("waiting for transactions")
	// for tribe consensus block validator
	ErrTribeNotAllowEmptyTxList                 = errors.New("tribe not allow empty tx list")
	ErrTribeMustContainChiefTx                  = errors.New("tribe must contain chief tx")
	ErrTribeChiefVolunteerLowBalance            = errors.New("tribe chief volunteer low balance")
	ErrTribeChiefVolunteerFail                  = errors.New("tribe chief volunteer check fail")
	ErrTribeChiefTxMustAtPositionZero           = errors.New("tribe chief tx must at postion 0")
	ErrTribeChiefTxSignerAndBlockSignerNotMatch = errors.New("tribe chief update tx signer and block signer not match")
	ErrTribeValdateTxSenderCannotInSignerList   = errors.New("tx sender cannot in signerlist")

	Chief100BlockReward, _                      = new(big.Int).SetString("35000000000000000000", 10) //Block reward in wei for successfully mining a block
	BlockRewardReducedInterval                  = 4505000                                            //half reward about two years
	SmartMeshFoundationAccount                  = common.HexToAddress("0xe0014a4268ad3d7b131551be47bc6ed72a6937e4")
	SmartMeshFoundationAccountDestroyBalance, _ = new(big.Int).SetString("376991118360000000000000000", 10)
)

type Tribe struct {
	accman   *accounts.Manager
	config   *params.TribeConfig // Consensus engine configuration parameters
	sigcache *lru.ARCCache       // mapping block.hash -> signer
	Status   *TribeStatus
	//SealErrorCounter uint32     // less then 3 , retry commit new work
	isInit bool
	lock   sync.Mutex
}

type API struct {
	accman *accounts.Manager
	chain  consensus.ChainReader
	tribe  *Tribe
}

// SignerFn is a signer callback function to request a hash to be signed by a
// backing account.
type SignerFn func(accounts.Account, []byte) ([]byte, error)

type Signer struct {
	Address common.Address `json:"address"` // 签名人
	Score   int64          `json:"score"`   // 分数
}

// append chief vsn 0.0.6
type Volunteer struct {
	Address common.Address `json:"address"` // 候选人
	Weight  int64          `json:"weight"`  // 权重
}

func (self *Signer) String() string {
	return fmt.Sprintf("%s:%d", self.Address.Hex(), self.Score)
}

type History struct {
	Number     int64          `json:"number"`
	Hash       common.Hash    `json:"hash"`
	Signer     common.Address `json:"signer"`
	Difficulty *big.Int       `json:"difficulty"`
	Timestamp  *big.Int       `json:"timestamp"`
}

type TribeStatus struct {
	Signers     []*Signer        `json:"signers"`
	Leaders     []common.Address `json:"leaders"`
	SignerLevel string           `json:"signerLevel"` // None -> Volunteer -> Signer
	Number      int64            `json:"number"`      // last block.number
	// for watch the set method result
	Epoch       *big.Int `json:"epoch"`
	LeaderLimit *big.Int `json:"leaderLimit"`
	SignerLimit *big.Int `json:"signerLimit"`
	Vsn         string   `json:"version"` // chief version

	blackList []common.Address
	nodeKey   *ecdsa.PrivateKey
	tribe     *Tribe
}

type TribeVolunteers struct {
	Length     *big.Int     `json:"length"`
	Volunteers []*Volunteer `json:"volunteers"`
}

type TribeMiner struct {
	Address common.Address `json:"address"`
	Balance *big.Int       `json:"balance"`
	Level   string         `json:"level"` // None 、 Volunteer 、 Signer
}
